#ifndef _GC_COMMON_CONFIG_H_
#define _GC_COMMON_CONFIG_H_

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <strings.h>
#include "util.h"
#include "list.h"

const char * DEFAULT_CONFIG_FILE = "/etc/great_config/great_config.conf";
const char * DEFAULT_CACHE_FILE = "/var/cache/great_config/great_config.json";
const char * DEFAULT_GROUP = "default";

typedef enum {GC_SRC_MYSQL = 1, GC_SRC_URL = 2, GC_SRC_FILE} gc_src;
typedef struct _gc_config_t gc_config_t;

struct _gc_config_t {
	gc_src src;
	char auto_cache;
	char * cache_file;
	char * group;
	char * data;
	gc_list_t *(* get_config) (const gc_config_t*);
};


#include "get_config_by_mysql.h"
#include "get_config_by_file.h"
#include "get_config_by_url.h"
#include "save_config_to_cache.h"

gc_list_t * gc_get_config_by_none(const gc_config_t * config) {
	return NULL;
}

inline static gc_src gc_get_src(const char * src) {
	if(strcasecmp(src, "mysql") == 0 || *src == '1') {
		return GC_SRC_MYSQL;
	} else if(strcasecmp(src, "url") == 0 || *src == '2') {
		return GC_SRC_URL;
	} else if(strcasecmp(src, "file") == 0 || *src == '3') {
		return GC_SRC_FILE;
	}
	return GC_SRC_URL;
}

void gc_config_free(gc_config_t * config) {
	if(config != NULL) {
		if(config->group != NULL) {
			free(config->group);
			config->group = NULL;
		}
		if(config->data != NULL) {
			free(config->data);
			config->data = NULL;
		}
		if(config->cache_file != NULL) {
			free(config->cache_file);
			config->cache_file = NULL;
		}
		free(config);
		config = NULL;
	}
}

inline static void gc_config_check(gc_config_t * config) {
	if(config->group == NULL) {
		config->group = strdup(DEFAULT_GROUP);
	}

	if(config->cache_file == NULL) {
		config->cache_file = strdup(DEFAULT_CACHE_FILE);
	}

	switch(config->src) {
		case GC_SRC_MYSQL:
			config->get_config = gc_get_config_by_mysql;
			break;
		case GC_SRC_URL:
			config->get_config = gc_get_config_by_url;
			break;
		case GC_SRC_FILE:
			config->get_config = gc_get_config_by_file;
			break;
		default:
			config->get_config = gc_get_config_by_none;
			fprintf(stderr, "%s:%d: gc_config::src cann't be empty, please check your config\n", __FILE__, __LINE__);
			break;
	}
	if(config->data == NULL) {
		fprintf(stderr, "%s:%d: gc_config::data cann't be empty, please check your config\n", __FILE__, __LINE__);
	}
}

gc_config_t * gc_config_load(const char * file) {
	char buffer[1024] = {0}, *buf = buffer;
	FILE *fp = NULL;
	int len = 0;
	char *pos = NULL;
	char *key = NULL, *value = NULL;

	if(file == NULL) {
		file = DEFAULT_CONFIG_FILE;
	}

	fp = fopen(file, "r");
	if(fp == NULL) {
		fprintf(stderr, "%s:%d open file %s failed: %s\n", __FILE__, __LINE__, file, strerror(errno));
		return NULL;
	}

	gc_config_t * config = (gc_config_t *) malloc(sizeof(gc_config_t));

	memset(config, 0, sizeof(gc_config_t));

	while(fgets(buf, sizeof(buffer), fp) != NULL) {
		len = strlen(buf);
		if(len == 0 || *buf == '#') {
			continue;
		}
		
		pos = strchr(buf, '=');

		if(pos != NULL) {
			*pos = '\0';

			key = gc_trim(buf);
			value = gc_trim(pos + 1);
			if(strcmp(key, "group") == 0) {
				config->group = strndup(value, strlen(value) + 1);
			} else if(strcmp(key, "source") == 0)  {
				config->src = gc_get_src(value);
			} else if(strcmp(key, "source_string") == 0) {
				config->data = strndup(value, strlen(value) + 1);
			} else if(strcmp(key, "cache_file") == 0) {
				config->cache_file = strndup(value, strlen(value) + 1);
			} else if(strcmp(key, "auto_cache") == 0) {
				config->auto_cache = value[0] == '1' ? '1' : '0';
			}
		}
		memset(buf, 0, sizeof(buffer));
	}

	gc_config_check(config);

	if(fp != NULL) {
		fclose(fp);
		fp = NULL;
	}
	return config;
}

gc_list_t * gc_get_config(const char * file) {
	gc_config_t * config = gc_config_load(file);	

	gc_list_t * list = NULL;
	if(config != NULL) {
		list = config->get_config(config);	
	}
	
	if(config->auto_cache == '1' && list != NULL && list->size > 0) {
		if(gc_save_config_to_cache(config, list) != 0) {
			fprintf(stderr, "%s:%d: save config to cache failed!\n", __FILE__, __LINE__);
		}
	} else if(list == NULL) {
		fprintf(stderr, "%s:%d: load config from %s failed, so load from cache %s\n", 
					__FILE__, __LINE__, config->data, config->cache_file);
		list = gc_load_config_from_cache(config);	
	}

	gc_config_free(config);

	return list;
}

void gc_config_print(gc_config_t * config) {
	printf("Current config is:\n");
	printf(">> SOURCE = %s\n", config->src == GC_SRC_MYSQL ? "mysql" : "url");
	printf(">> DATA = %s\n", config->data);
	printf(">> GROUP = %s\n", config->group);
}

#endif
